#include <fstream>
#include <iostream>
#include <ctime>
#include "library.h"

using namespace std;

// BOOKS

void Library::addBook(const Book &b) throw(Exceptions){
    if(books.empty())
        books.push_front(b);
    else
    {
        list<Book>::iterator i = books.begin();
        for(; i != books.end() && *i < b; i++);
        if( !(b == *i) ) books.insert(i,b);
        else
            throw B_DATA_REDUNDANCY;
    }
}

void Library::delBook(const string& id) throw(Exceptions){
    list<Book>::iterator i = books.begin();
    for(; i != books.end() && (*i).getBookId() != id; i++);
    if( i != books.end()) books.remove(*i);
    else
        throw B_DATA_DOESNOTEXIST;
}

void Library::searchBook(const string& type, const string& key) const {
    for(list<Book>::const_iterator i = books.begin(); i != books.end(); i++) {
        if(type == "Author" && key == (*i).getAuthor()) cout << *i << endl;
        else if(type == "Title" && key == (*i).getTitle()) cout << *i << endl;
        else if(type == "Id" && key == (*i).getBookId()) cout << *i << endl;
        else if(type == "Out" && (*i).getPieceInStock() != (*i).getPieceSum()) cout << *i << endl;
    }
}

void Library::fillBooksFromFile(const string &fname) throw(Exceptions) {
    ifstream inp;
    inp.open(fname.c_str());

    if(inp.fail())
        throw B_FILEDONOTEXIST;
    else {
        Book b;
        inp >> b;
        while(!inp.eof()) {
            books.push_back(b);
            inp >> b;
        }
    }
}

void Library::listBooks() const {
    for(list<Book>::const_iterator i = books.begin(); i != books.end(); i++) {
        cout << *i << endl;
    }
}

void Library::saveDataOfBooks(const string &fname) const {
    ofstream out;
    out.open(fname.c_str());

    for(list<Book>::const_iterator i = books.begin(); i != books.end(); i++) {
        out << *i;
    }
}

// MEMBERS

void Library::payment(const std::string &id,const int &val) throw(Exceptions){
    bool l = true;

    for(list<Student>::iterator i = members01.begin(); i != members01.end() && l; i++)
        if(id == (*i).getId()) { (*i).setBalance(val); l = false; }
    for(list<Teacher>::iterator i = members02.begin(); i != members02.end() && l; i++)
        if(id == (*i).getId()) { (*i).setBalance(val); l = false; }
    for(list<StudyOfOtherUni>::iterator i = members03.begin(); i != members03.end() && l; i++)
        if(id == (*i).getId()) { (*i).setBalance(val); l = false; }
    for(list<Other>::iterator i = members04.begin(); i != members04.end() && l; i++)
        if(id == (*i).getId()) { (*i).setBalance(val); l = false; }

    if(l) throw M_DATA_DOESNOTEXIST;
}

void Library::addMember(const string &typ, stringstream &inp) throw(Exceptions){
    if(typ == "Student")
    {
        Student m;
        inp >> m;

        if(members01.empty())
            members01.push_front(m);
        else
        {
            list<Student>::iterator i = members01.begin();
            for(; i != members01.end() && *i < m; i++);
            if( !(m == *i) ) members01.insert(i,m);
            else
                throw M_DATA_REDUNDANCY;
        }
    }
    else if (typ == "Teacher")
    {
        Teacher m;
        inp >> m;

        if(members02.empty())
            members02.push_front(m);
        else
        {
            list<Teacher>::iterator i = members02.begin();
            for(; i != members02.end() && *i < m; i++);
            if( !(m == *i) ) members02.insert(i,m);
            else
                throw M_DATA_REDUNDANCY;
        }
    }
    else if (typ == "SOOU")
    {
        StudyOfOtherUni m;
        inp >> m;

        if(members03.empty())
            members03.push_front(m);
        else
        {
            list<StudyOfOtherUni>::iterator i = members03.begin();
            for(; i != members03.end() && *i < m; i++);
            if( !(m == *i) ) members03.insert(i,m);
            else
                throw M_DATA_REDUNDANCY;
        }
    }
    else if (typ == "Other")
    {
        Other m;
        inp >> m;

        if(members04.empty())
            members04.push_front(m);
        else
        {
            list<Other>::iterator i = members04.begin();
            for(; i != members04.end() && *i < m; i++);
            if( !(m == *i) ) members04.insert(i,m);
            else
                throw M_DATA_REDUNDANCY;
        }
    }
}

void Library::delMember(const string& id) throw(Exceptions) {
    bool l = true;

    if(l)
    {
        list<Student>::iterator i = members01.begin();
        for(; i != members01.end() && (*i).getId() != id; i++);
        if( i != members01.end()) { members01.remove(*i); l = false; }
    }
    if(l)
    {
        list<Teacher>::iterator i = members02.begin();
        for(; i != members02.end() && (*i).getId() != id; i++);
        if( i != members02.end()) { members02.remove(*i); l = false; }
    }
    if(l)
    {
        list<StudyOfOtherUni>::iterator i = members03.begin();
        for(; i != members03.end() && (*i).getId() != id; i++);
        if( i != members03.end()) { members03.remove(*i); l = false; }
    }
    if(l)
    {
        list<Other>::iterator i = members04.begin();
        for(; i != members04.end() && (*i).getId() != id; i++);
        if( i != members04.end()) { members04.remove(*i); l = false; }
    }

    if(l) throw M_DATA_DOESNOTEXIST;
}

void Library::searchMember(const string& type, const string& key) const {
    if(type == "Student")
        for(list<Student>::const_iterator i = members01.begin(); i != members01.end(); i++) cout << *i << endl;
    else if(type == "Teacher")
        for(list<Teacher>::const_iterator i = members02.begin(); i != members02.end(); i++) cout << *i << endl;
    else if(type == "SOOU")
        for(list<StudyOfOtherUni>::const_iterator i = members03.begin(); i != members03.end(); i++) cout << *i << endl;
    else if(type == "Other")
        for(list<Other>::const_iterator i = members04.begin(); i != members04.end(); i++) cout << *i << endl;
    else if(type == "Name")
    {
        for(list<Student>::const_iterator i = members01.begin(); i != members01.end(); i++)
            if(key == (*i).getName()) cout << *i << endl;
        for(list<Teacher>::const_iterator i = members02.begin(); i != members02.end(); i++)
            if(key == (*i).getName()) cout << *i << endl;
        for(list<StudyOfOtherUni>::const_iterator i = members03.begin(); i != members03.end(); i++)
            if(key == (*i).getName()) cout << *i << endl;
        for(list<Other>::const_iterator i = members04.begin(); i != members04.end(); i++)
            if(key == (*i).getName()) cout << *i << endl;
    }
    else if(type == "Id")
    {
        for(list<Student>::const_iterator i = members01.begin(); i != members01.end(); i++)
            if(key == (*i).getId()) cout << *i << endl;
        for(list<Teacher>::const_iterator i = members02.begin(); i != members02.end(); i++)
            if(key == (*i).getId()) cout << *i << endl;
        for(list<StudyOfOtherUni>::const_iterator i = members03.begin(); i != members03.end(); i++)
            if(key == (*i).getId()) cout << *i << endl;
        for(list<Other>::const_iterator i = members04.begin(); i != members04.end(); i++)
            if(key == (*i).getId()) cout << *i << endl;
    }
    else if(type == "Late")
    {
        for(list<Rental>::const_iterator i = rentals.begin(); i != rentals.end(); i++)
        {
            if((*i).getTo() == 0)
            {
                // get current date
                time_t a, b;
                double diff;

                time ( &b );
                a = (*i).getFrom();

                diff = difftime(b, a);
                diff = diff/60/60/24/31;

                if(diff > 1)
                {
                    bool l = false;
                    list<Teacher>::const_iterator it = members02.begin();
                    for(; it != members02.end() && !(l = (*i).getM_ID() == (*it).getId()); ++it);
                    if(l && diff > 12) cout << *it << endl;
                    else if(!l)
                    {
                        list<Student>::const_iterator it1 = members01.begin();
                        for(; it1 != members01.end() && !(l = (*i).getM_ID() == (*it1).getId()); ++it1);
                        if(l) cout << *it1 << endl;
                        else
                        {
                            list<StudyOfOtherUni>::const_iterator it3 = members03.begin();
                            for(; it3 != members03.end() && !(l = (*i).getM_ID() == (*it3).getId()); ++it3);
                            if(l) cout << *it3 << endl;
                            else
                            {
                                list<Other>::const_iterator it4 = members04.begin();
                                for(; it4 != members04.end() && !(l = (*i).getM_ID() == (*it4).getId()); ++it4);
                                if(l) cout << *it4 << endl;
                            }
                        }
                    }
                }
            }
        }
    }
}

void Library::fillMembersFromFile(const string &fname) throw(Exceptions) {
    ifstream inp;
    inp.open(fname.c_str());

    if(inp.fail())
        throw M_FILEDONOTEXIST;
    else {
        string typ;
        inp >> typ;

        while(!inp.eof()) {
            if(typ == "Student")
                { Student m; inp >> m; members01.push_back(m); }
            else if (typ == "Teacher")
                { Teacher m; inp >> m; members02.push_back(m); }
            else if (typ == "SOOU")
                { StudyOfOtherUni m; inp >> m; members03.push_back(m); }
            else
                { Other m; inp >> m; members04.push_back(m); }

            inp >> typ;
        }
    }
}

void Library::listMembers() const {
    for(list<Student>::const_iterator i = members01.begin(); i != members01.end(); i++) cout << *i << endl;
    for(list<Teacher>::const_iterator i = members02.begin(); i != members02.end(); i++) cout << *i << endl;
    for(list<StudyOfOtherUni>::const_iterator i = members03.begin(); i != members03.end(); i++) cout << *i << endl;
    for(list<Other>::const_iterator i = members04.begin(); i != members04.end(); i++) cout << *i << endl;
}

void Library::saveDataOfMembers(const string &fname) const {
    ofstream out;
    out.open(fname.c_str());

    for(list<Student>::const_iterator i = members01.begin(); i != members01.end(); i++) {
        out << *i;
    }
    for(list<Teacher>::const_iterator i = members02.begin(); i != members02.end(); i++) {
        out << *i;
    }
    for(list<StudyOfOtherUni>::const_iterator i = members03.begin(); i != members03.end(); i++) {
        out << *i;
    }
    for(list<Other>::const_iterator i = members04.begin(); i != members04.end(); i++) {
        out << *i;
    }
}

// RENTALS

void Library::addRentalOut(const Rental& r) throw(Exceptions){
    bool l = false;

    // Book isExist ?
    list<Book>::iterator i = books.begin();
    for(; i != books.end() && !(l = (*i).getBookId() == r.getB_ID()); ++i);
    // OK => Piece isEnough && isRent ?
    if(l) l = (*i).getPieceInStock() >= 1 && (*i).getToRent();
    else throw B_DATA_DOESNOTEXIST;
    // OK => Member isExist && !isOwe ?
    if(l) {
        l = false;

        list<Student>::iterator it1 = members01.begin();
        for(; it1 != members01.end() && !(l = (*it1).getId() == r.getM_ID()); it1++);
        if(l)
        {
            l = (*it1).getBalance() >= 0;
            // reduction of member's balance
            if(l) (*it1).setBalance(-(*it1).rentCostCalc());
            else throw OWE;
        }
        else
        {
            list<Teacher>::iterator it2 = members02.begin();
            for(; it2 != members02.end() && !(l = (*it2).getId() == r.getM_ID()); it2++);
            if(l)
            {
                l = (*it2).getBalance() >= 0;
                // reduction of member's balance
                if(l) (*it2).setBalance(-(*it2).rentCostCalc());
                else throw OWE;
            }
            else
            {
                list<StudyOfOtherUni>::iterator it3 = members03.begin();
                for(; it3 != members03.end() && !(l = (*it3).getId() == r.getM_ID()); it3++);
                if(l)
                {
                    l = (*it3).getBalance() >= 0;
                    // reduction of member's balance
                    if(l) (*it3).setBalance(-(*it3).rentCostCalc());
                    else throw OWE;
                }
                else
                {
                    list<Other>::iterator it4 = members04.begin();
                    for(; it4 != members04.end() && !(l = (*it4).getId() == r.getM_ID()); it4++);
                    if(l)
                    {
                        l = (*it4).getBalance() >= 0;
                        // reduction of member's balance
                        if(l) (*it4).setBalance(-(*it4).rentCostCalc());
                        else throw OWE;
                    }
                }
            }
        }
    }
    else throw ISNOTRENT;

    if(l) {
        // reduction of Piece in stock
        int v = (*i).getPieceInStock() - 1;
        (*i).setPieceInStock(v);
        // add
        rentals.push_back(r);
    }
    else
        throw M_DATA_DOESNOTEXIST;
}

void Library::addRentalIn(const Rental& r) throw(Exceptions){
    bool l = false;

    // Rental isExist ?
    list<Rental>::iterator i = rentals.begin();
    for(; i != rentals.end() && !(l = (*i).getB_ID() == r.getB_ID() &&
                                      (*i).getM_ID() == r.getM_ID() &&
                                      (*i).getTo() == 0 ); i++);

    if(l) {
        // reduction of Piece in stock
        l = false;

        list<Book>::iterator it = books.begin();
        for(; it != books.end() && !(l = (*it).getBookId() == r.getB_ID()); it++);
        if(l)
        {
            int v = (*it).getPieceInStock() + 1;
            (*it).setPieceInStock(v);
        }

        // get current date
        time_t rawtime;

        time ( &rawtime );

        // change rental
        (*i).setTo(rawtime);

        // check delay
        time_t a, b;
        double diff;

        a = (*i).getFrom();
        b = (*i).getTo();
        diff = difftime(b, a);
        diff = diff/60/60/24/31;

        l = false;
        list<Student>::iterator it1 = members01.begin();
        for(; it1 != members01.end() && !(l = (*it1).getId() == r.getM_ID()); it1++);
        if(l)
        {
            diff = (*it1).rentTimeCalc() - diff;
            if(diff < 0)
            {
                int b;
                b = (-1) * diff + 0.9;
                b *= (*it1).delayCostCalc();
                (*it1).setBalance(-b);
            }
        }
        else
        {
            list<Teacher>::iterator it2 = members02.begin();
            for(; it2 != members02.end() && !(l = (*it2).getId() == r.getM_ID()); it2++);
            if(l)
            {
                diff = (*it2).rentTimeCalc() - diff;
                if(diff < 0)
                {
                    int b;
                    b = (-1) * diff + 0.9;
                    b *= (*it2).delayCostCalc();
                    (*it2).setBalance(-b);
                }
            }
            else
            {
                list<StudyOfOtherUni>::iterator it3 = members03.begin();
                for(; it3 != members03.end() && !(l = (*it3).getId() == r.getM_ID()); it3++);
                if(l)
                {
                    diff = (*it3).rentTimeCalc() - diff;
                    if(diff < 0)
                    {
                        int b;
                        b = (-1) * diff + 0.9;
                        b *= (*it3).delayCostCalc();
                        (*it3).setBalance(-b);
                    }
                }
                else
                {
                    list<Other>::iterator it4 = members04.begin();
                    for(; it4 != members04.end() && !(l = (*it4).getId() == r.getM_ID()); it4++);
                    if(l)
                    {
                        diff = (*it4).rentTimeCalc() - diff;
                        if(diff < 0)
                        {
                            int b;
                            b = (-1) * diff + 0.9;
                            b *= (*it4).delayCostCalc();
                            (*it4).setBalance(-b);
                        }
                    }
                }
            }
        }
    }
    else
        throw R_DATA_DOESNOTEXIST;
}

void Library::fillRentalsFromFile(const string &fname) throw(Exceptions) {
    ifstream inp;
    inp.open(fname.c_str());

    if(inp.fail())
        throw R_FILEDONOTEXIST;
    else {
        Rental r;
        inp >> r;
        while(!inp.eof()) {
            rentals.push_back(r);
            inp >> r;
        }
    }
}

void Library::saveDataOfRentals(const string &fname) const {
    ofstream out;
    out.open(fname.c_str());

    for(list<Rental>::const_iterator i = rentals.begin(); i != rentals.end(); i++) {
        out << *i;
    }
}

void Library::searchRental(const std::string& id) const throw(Exceptions) {
    bool l = false;

    for(list<Rental>::const_iterator i = rentals.begin(); i != rentals.end(); ++i)
    {
        l = id == (*i).getM_ID();
        if(l) cout << *i << endl;
    }

    if(!l) throw M_DATA_DOESNOTEXIST;
}

